{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "在深度学习框架中，广播机制和反向传播机制是两个不同的概念，分别用于解决不同的问题。下面我将分别解释这两个机制：\n",
    "\n",
    "### 广播机制（Broadcasting）\n",
    "\n",
    "广播机制主要用于处理不同形状的数组之间的算术运算。当两个数组的形状不完全相同但可以通过扩展来匹配时，广播机制会自动扩展较小的数组，使其形状与较大的数组匹配，从而允许进行元素级别的运算。\n",
    "\n",
    "#### 示例\n",
    "假设我们有两个数组 `A` 和 `B`，其中 `A` 的形状为 `(3, 4)`，而 `B` 的形状为 `(4,)`：\n",
    "\n",
    "```python\n",
    "import numpy as np\n",
    "\n",
    "A = np.array([[1, 2, 3, 4],\n",
    "              [5, 6, 7, 8],\n",
    "              [9, 10, 11, 12]])\n",
    "\n",
    "B = np.array([1, 2, 3, 4])\n",
    "```\n",
    "\n",
    "当我们执行 `A + B` 时，广播机制会将 `B` 扩展为形状 `(3, 4)` 的数组，具体如下：\n",
    "\n",
    "```python\n",
    "B_expanded = np.array([[1, 2, 3, 4],\n",
    "                       [1, 2, 3, 4],\n",
    "                       [1, 2, 3, 4]])\n",
    "```\n",
    "\n",
    "然后进行元素级别的加法运算：\n",
    "\n",
    "```python\n",
    "result = A + B_expanded\n",
    "```\n",
    "\n",
    "最终结果为：\n",
    "\n",
    "```python\n",
    "result = np.array([[2, 4, 6, 8],\n",
    "                   [6, 8, 10, 12],\n",
    "                   [10, 12, 14, 16]])\n",
    "```\n",
    "\n",
    "### 反向传播机制（Backpropagation）\n",
    "\n",
    "反向传播机制是深度学习中用于计算梯度的主要算法。它通过链式法则从输出层向输入层逐层传递误差，从而计算每个参数的梯度，进而更新模型参数以最小化损失函数。\n",
    "\n",
    "#### 原理\n",
    "1. **前向传播**：首先，输入数据通过神经网络进行前向传播，计算每一层的输出。\n",
    "2. **计算损失**：在输出层计算预测值与真实值之间的损失。\n",
    "3. **反向传播**：从输出层开始，逐层向前传递误差，计算每层参数的梯度。\n",
    "4. **参数更新**：使用优化算法（如梯度下降）根据计算出的梯度更新模型参数。\n",
    "\n",
    "#### 示例\n",
    "假设有一个简单的神经网络，包含一个输入层、一个隐藏层和一个输出层。前向传播过程如下：\n",
    "\n",
    "1. 输入层到隐藏层的线性变换：\\( z_1 = W_1 x + b_1 \\)\n",
    "2. 激活函数：\\( a_1 = \\sigma(z_1) \\)\n",
    "3. 隐藏层到输出层的线性变换：\\( z_2 = W_2 a_1 + b_2 \\)\n",
    "4. 输出层激活函数（假设为线性）：\\( y = z_2 \\)\n",
    "\n",
    "反向传播过程如下：\n",
    "\n",
    "1. 计算输出层的误差：\\( \\delta_2 = (y - t) \\)\n",
    "2. 计算输出层到隐藏层的梯度：\\( \\frac{\\partial L}{\\partial W_2} = \\delta_2 a_1^T \\)\n",
    "3. 计算隐藏层的误差：\\( \\delta_1 = W_2^T \\delta_2 \\odot \\sigma'(z_1) \\)\n",
    "4. 计算隐藏层到输入层的梯度：\\( \\frac{\\partial L}{\\partial W_1} = \\delta_1 x^T \\)\n",
    "\n",
    "最后，使用梯度下降法更新参数：\n",
    "\n",
    "```python\n",
    "W_2 = W_2 - \\alpha \\frac{\\partial L}{\\partial W_2}\n",
    "b_2 = b_2 - \\alpha \\delta_2\n",
    "W_1 = W_1 - \\alpha \\frac{\\partial L}{\\partial W_1}\n",
    "b_1 = b_1 - \\alpha \\delta_1\n",
    "```\n",
    "\n",
    "其中，\\(\\alpha\\) 是学习率。\n",
    "\n",
    "### 总结\n",
    "\n",
    "- **广播机制**：用于处理不同形状数组之间的算术运算，通过扩展较小的数组来匹配较大的数组。\n",
    "- **反向传播机制**：用于计算神经网络中每个参数的梯度，以便通过优化算法更新参数，从而最小化损失函数。\n",
    "\n",
    "希望这些解释对你有所帮助！如果有任何进一步的问题，请随时提问。"
   ],
   "id": "ec561eacdae42475"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## 1、导包",
   "id": "394cdafe8683846"
  },
  {
   "cell_type": "code",
   "id": "20ea4196a33f035b",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-06T13:56:51.932871Z",
     "start_time": "2024-11-06T13:56:47.845481Z"
    }
   },
   "source": [
    "import torch\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np"
   ],
   "outputs": [],
   "execution_count": 1
  },
  {
   "cell_type": "markdown",
   "id": "1cd1f530d6589346",
   "metadata": {},
   "source": [
    "## 2、产生数据集\n",
    "y = Xw + b + egx\n",
    " \n",
    "w = [2,-3.4]\n",
    "\n",
    "b = 4.2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "70d7a9bf58f37318",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T01:42:20.996061Z",
     "start_time": "2024-11-05T01:42:20.988197Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(torch.Size([1000, 2]), torch.Size([1000, 1]))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def linear_data(w,b,num_examples):\n",
    "    X = torch.normal(0,1,[num_examples,len(w)]) # 正态分布\n",
    "    y = torch.matmul(X, w) + b # 矩阵乘法\n",
    "    y += torch.normal(0,0.01,y.shape) # 截距\n",
    "    return X, y.reshape(-1,1)\n",
    "w = torch.tensor([2,-3.4])\n",
    "b = 4.2\n",
    "X, y = linear_data(w,b,1000)\n",
    "X.shape, y.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1da8b71ad7f350e9",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T01:42:23.640060Z",
     "start_time": "2024-11-05T01:42:23.482835Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x1179b65d0>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.scatter(X[:,1],y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "586ee1597e96ee01",
   "metadata": {},
   "source": [
    "## 3、读取数据集\n",
    "训练模型时要对数据集进行遍历，每次抽取小批量样本，并使用他们来更新我们的模型\n",
    "\n",
    "在 Python 中，yield 关键字用于定义生成器（generator）函数。生成器是一种特殊的迭代器，它可以“惰性地”生成数据，即在需要时才生成下一个值，而不是一次性生成所有值。这种特性使得生成器非常适合处理大数据集或无限序列，因为它们不会一次性占用大量内存。\n",
    "\n",
    "基本概念\n",
    "- 生成器函数：包含 yield 语句的函数被称为生成器函数。当调用生成器函数时，它不会立即执行函数体中的代码，而是返回一个生成器对象。\n",
    "- 生成器对象：生成器对象是一个可以迭代的对象，每次调用 next() 方法或使用 for 循环时，生成器会从上次停止的地方继续执行，直到遇到下一个 yield 语句。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "100fc23c142e7b9d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T02:05:04.479593Z",
     "start_time": "2024-11-05T02:05:04.471754Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([10, 2]) torch.Size([10, 1])\n"
     ]
    }
   ],
   "source": [
    "def data_iter(batch_size, X, y):\n",
    "    num_examples = len(X)\n",
    "    indices = list(range(num_examples)) # 产生 0 - len的整数\n",
    "    np.random.shuffle(indices) # 打乱顺序\n",
    "    for i in range(0, num_examples, batch_size):\n",
    "        batch_indices = torch.tensor(indices[i : min(i+batch_size, num_examples)]) # min(i+batch_size, num_examples) 不可超过最大下标\n",
    "        yield X[batch_indices], y[batch_indices]\n",
    "batch_size = 10\n",
    "for feature,label in data_iter(batch_size, X, y)  :\n",
    "    print(feature.shape, label.shape)\n",
    "    break;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51af17e18a859dd7",
   "metadata": {},
   "source": [
    "`注意：当执行迭代时，会连续的获得不同的小批次，直至遍历完整个数据集。上面实现的迭代对教学来说很合适，但它执行效率很低，可能会在实际的问题中陷入麻烦。例如，它要求我们将所有的数据加载到内存中，并执行大龄的随机内存访问。在深度学习框架中实现内置迭代器的效率要高很多，它可以处理存储在文件中的数据和数据流提供的数据。`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e2db743a5188a12",
   "metadata": {},
   "source": [
    "## 4、初始化模型参数\n",
    "通过均值为0,标准差为0.01的正态分布中抽取随机数来初始化权重，并将偏置初始化为0\n",
    "任务：更新w和b的值，直到这些参数足以拟合数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1691108c12119e62",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T02:12:05.597576Z",
     "start_time": "2024-11-05T02:12:05.592004Z"
    }
   },
   "outputs": [],
   "source": [
    "w = torch.normal(0,0.01,size=(2,1), requires_grad=True) # 因为X.shape=(10,2)有2个维度, 所有w的size为(2,1)有2个数据分别与x运算，requires_grad获取偏导数\n",
    "b = torch.zeros(1, requires_grad=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a90e1f2231f9c90e",
   "metadata": {},
   "source": [
    "## 5、定义模型\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6b2180384db222ca",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T02:46:55.594101Z",
     "start_time": "2024-11-05T02:46:55.589181Z"
    }
   },
   "outputs": [],
   "source": [
    "def linear(X,w,b):\n",
    "    return torch.matmul(X,w) + b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2935aa2641df4dd6",
   "metadata": {},
   "source": [
    "## 6、定义损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "2a2016e9b6eeda0f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T02:47:24.973617Z",
     "start_time": "2024-11-05T02:47:24.969411Z"
    }
   },
   "outputs": [],
   "source": [
    "def sequared_loss(y_hat, y):\n",
    "    return (y_hat - y.reshape(y_hat.shape))**2 / 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c6e00706eb2bca2",
   "metadata": {},
   "source": [
    "## 7、定义优化算法\n",
    "小批量随机梯度下降"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f11bfb9c4283ddcd",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T02:47:28.230216Z",
     "start_time": "2024-11-05T02:47:28.224289Z"
    }
   },
   "outputs": [],
   "source": [
    "def sgd(params,lr,batch_size):\n",
    "    \"\"\"\n",
    "    j = j - lr * g(x)\n",
    "    小批量随机梯度下降\n",
    "    :param params: \n",
    "    :param lr: 学习率\n",
    "    :param batch_size: \n",
    "    :return: \n",
    "    \"\"\"\n",
    "    with torch.no_grad(): # 禁用梯度计算\n",
    "        for param in params:\n",
    "            # 参数更新公式为，这里除以 batch_size 是因为在一次前向传播中，\n",
    "            # 梯度是对整个批量数据求和的结果，所以需要平均化以确保学习率的一致性。\n",
    "            param -= lr / batch_size * param.grad \n",
    "            param.grad.zero_() # 清空梯度"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9293e9fd8c7cd8a",
   "metadata": {},
   "source": [
    "## 8、训练\n",
    "在每轮中，使用data_iter函数遍历整个数据集，并将训练数据集中的所有样本都使用一次（假设样本能被批量大小整除），这里的轮数 num_epochs 和 学习率 lr 为超参数，分别为3和0.03."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "46987306ca781d4",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-11-05T02:50:56.273044Z",
     "start_time": "2024-11-05T02:50:56.264420Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 1, loss 0.035\n",
      "epoch 2, loss 0.000\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 3, loss 0.000\n"
     ]
    }
   ],
   "source": [
    "lr = 0.03 # 学习率\n",
    "num_epochs = 3 # 训练轮数\n",
    "net = linear # 线性方程\n",
    "loss = sequared_loss # 损失函数\n",
    "for epoch in range(num_epochs):\n",
    "    for feature,label in data_iter(batch_size, X, y):\n",
    "        # print(feature.shape, label.shape)\n",
    "        ret = net(feature,w,b);\n",
    "        l = loss(ret,label) # X和y的最小损失\n",
    "        # 因为l的shape是(batch_size,1)，而不是一个标量；l中所有的元素被加在一起，并以此计算[w,b]的梯度。\n",
    "        l.sum().backward()\n",
    "        sgd([w,b],lr,batch_size)\n",
    "    with torch.no_grad():\n",
    "        train_loss = loss(net(X,w,b),y)\n",
    "        print('epoch %d, loss %.3f' % (epoch + 1, float(train_loss.mean())))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "184df46788ea5f42",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "pytorch"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
